export default class UniqueArray {
    constructor(key) {
        this._key = key ?? 'id';
        this._arr = [];
        this._map = new Map();
    }

    Insert(value, position) {
        if(this.Has(value)) return false;
        position < 0 ? this._arr.unshift(value) : this._arr.push(value);
        this._map.set(value[this._key], value);
        return true;
    }

    InsertToIndex(value, i) {
        if(this.Has(value)) return false;
        this._arr.splice(i, 0, value);
        this._map.set(value[this._key], value);
        return true;
    }

    InsertToPosition(A, B, position) {
        if(A === B) return false;
        if(this.Has(A)) return false;
        const _iB = this._arr.indexOf(B);
        if(_iB >= 0) {
            if(position >= 0) {
                this._arr.splice(_iB + 1, 0, A);
            } else {
                this._arr.splice(_iB, 0, A);
            }
            this._map.set(A[this._key], A);
            return true;
        }
        return false;
    }

    Pop() {
        let _ret = this._arr.pop();
        if(_ret) {
            this._map.delete(_ret[this._key]);
        }
        return _ret;
    }

    Erase(value) {
        const _i = this._arr.indexOf(value);
        if(_i >= 0) {
            this._arr.splice(_i, 1);
            this._map.delete(value[this._key]);
            return true;
        }
        return false;
    };
    EraseByKey(key) {
        let _value = this.GetByKey(key);
        if(_value) {
            this.Erase(_value);
        }
        return _value;
    }

    Clear() {
        this._arr.length = 0;
        this._map.clear();
    }

    GetByIndex(index) {
        if(index < 0 || index >= this.length) return undefined;
        return this._arr[index];
    }

    GetByKey(key) {
        return this._map.get(key);
    }

    GetLast() {
        const _len = this.length;
        if(_len > 0) {
            return this._arr[_len - 1];
        } else return undefined;
    }

    GetIndex(value){
        return this._arr.indexOf(value);
    }
    GetIndexByKey(key){
        const _value = this._map.get(key);
        if(_value) {
            return this._arr.indexOf(_value);
        }
        return -1;
    }

    SwitchTwo(A, B) {
        if(A === B) return false;
        const _iA = this._arr.indexOf(A);
        const _iB = this._arr.indexOf(B);
        if(_iA >= 0 && _iB >= 0) {
            this._arr[_iA] = B;
            this._arr[_iB] = A;
            return true;
        }
        return false;
    }

    MoveToIndex(A, index) {
        if(index >= 0 && index < this.length) {
            const _iA = this._arr.indexOf(A);
            if(_iA >= 0) {
                this._arr.splice(_iA, 1);
                this._arr.splice(index, 0, A);
                return true;
            }
        }
        return false;
    }

    MoveToPosition(A, B, position) {
        if(A === B) return false;
        const _iA = this._arr.indexOf(A);
        const _iB = this._arr.indexOf(B);
        if(_iA >= 0 && _iB >= 0) {
            this._arr.splice(_iA, 1);
            if(position >= 0) {
                this._arr.splice(_iB + 1, 0, A);
            } else {
                this._arr.splice(_iB, 0, A);
            }
            return true;
        }
        return false;
    }

    MoveHigher(A) {
        const _iA = this._arr.indexOf(A);
        if(_iA >= 1) {
            this._arr.splice(_iA, 1);
            this._arr.splice(_iA - 1, 0, A);
            return _iA - 1;
        }
        return -1;
    }

    MoveLower(A) {
        const _iA = this._arr.indexOf(A);
        if(_iA >= 0) {
            if(_iA < this._arr.length - 1) {
                this._arr.splice(_iA, 1);
                this._arr.splice(_iA + 1, 0, A);
            }
            return _iA + 1;
        }
        return -1;
    }

    Has(value) {
        return this._map.has(value[this._key]);
    }

    ForEach(fn, thisArg) {
        for(let i = 0, N = this._arr.length; i < N; ++i) {
            if(fn.call(thisArg, this._arr[i], i)) break;
        }
    }

    ForEachInverse(fn, thisArg) {
        for(let i = this._arr.length - 1; i >= 0; --i) {
            if(fn.call(thisArg, this._arr[i], i)) break;
        }
    }

    get length() {return this._arr.length;}

    DebugPrint(msg) {
        console.group(msg);
        for(let i = 0, N = this._arr.length; i < N; ++i) {
            console.log('key:', this._arr[i][this._key]);
        }
        console.groupEnd();
    }
}
